package JVM;

import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author shy_black
 * @date 2019/3/20 13:52
 * @Description:深浅拷贝---序列化 序列化-->把一个对象变成一个二进制的流
 * <p>
 * <p>
 * <p>
 * 深浅拷贝（对象）：
 * Object类提供的clone()方法：
 * protected native Object clone() throws CloneNotSupportedException;
 * protected权限，仅供子类使用
 * 要想让对象具备拷贝功能，必须实现Cloneable接口（标识接口，表示此类允许被克隆）：
 * -->public interface Cloneable {}//只有接口名称，没有抽象方法，标识接口
 * 并且在类中自定义clone调用Object类提供的继承权限clone方法
 * <p>
 * <p>
 * <p>
 * Cloneable：CloneNotSupportedException
 * 只有子类实现了Cloneable接口才可以使用Object类提供的clone方法
 * <p>
 * 浅拷贝：
 * 对象值的拷贝--》拷贝出来的对象任然保留原有对象的所有引用，
 * 普通数据类型--》复制，引用数据类型--》复制原有的地址，不产生新对象
 * 问题:只要任意一个拷贝对象或原对象的引用值发生改变，所有对象均会受到影响
 * <p>
 * 深拷贝：
 * 拷贝出来的对象产生了所有引用的新的对象
 * 特点：修改任意一个对象，不会对其他对象产生影响
 * 实现深拷贝：
 * 1.包含的其他类继续实现Cloneable接口，并且调用clone()方法(递归实现clone)
 * 2.使用序列化(***********)
 * 使用序列化深拷贝时，无序实现Cloneable接口，只需要实现Serializable接口
 * <p>
 * 序列化：将内存中保存的对象变为二进制流进行输出或者保存在文本中，将对象变为二进制流
 * 要想让类支持序列化，必须实现Serializable接口（标识接口）
 * Serializable接口为标识接口，只有实现了Serializable接口的类才具备对象序列化的功能
 * <p>
 * 具体实现序列化与反序列化，需要使用io包中的俩个处理类：
 * 序列化类：  ObjectOutputStream：
 * public ObjectOutputStream(OutputStream out) throws IOException
 * writeObject(Object obj):将obj变为二进制流输出到目标终端
 * 反序列化类：ObjectInputStream：
 * 选择反序列化的目标终端
 * public ObjectInputStream(InputStream in) throws IOException
 * public final Object readObject():将二进制流转换回Object对象
 * transient关键字：
 *      若希望类中的若干属性不被序列化，可以在属性前添加transient关键字
 * 延迟拷贝：浅拷贝+深拷贝
 *
 *
 */
public class 深浅拷贝 {
    public static void main(String[] args) throws Exception {

        Teacher teacher = new Teacher("张三","Java");
        Student stu = new Student("shy_black",18,teacher);
        Student stuClone = (Student)stu.cloneObject();
        System.out.println(stu);
        System.out.println(stuClone);
        System.out.println(teacher.equals(stuClone.getTeacher()) );


//        Person per = new Person("shy_black", 18);
//        File file = new File("C:\\Users\\shy_black\\Desktop\\java.txt");
//        OutputStream out = new FileOutputStream(file);
//        ObjectOutputStream oos = new ObjectOutputStream(out);
//        oos.writeObject(per);
//        out.close();
//        oos.close();
//
//        InputStream in = new FileInputStream(file);
//        ObjectInputStream ois = new ObjectInputStream(in);
//        Person per1 = (Person) ois.readObject();
//        System.out.println(per1);


//        //Cloneable
//        Teacher teacher = new Teacher("我","java");
//        Student student = new Student("shy" ,18,teacher);
//
//        Student studentClone = student.clone();
//        System.out.println(studentClone);
//        System.out.println(student);
    }
}

class Person implements Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

class Teacher implements Cloneable, Serializable {
    private String name;
    private String job;

    public Teacher(String name, String job) {
        this.name = name;
        this.job = job;
    }

    public Teacher clone() {
        Teacher teacher = null;
        try {
            teacher = (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return teacher;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public String getName() {
        return name;
    }

    public String getJob() {
        return job;
    }
}

class Student implements Cloneable, Serializable {
    private String name;
    private int age;
    private Teacher teacher;

    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }

    public Student clone() {
        //递归实现Cloneable完成深拷贝
        Student student = null;
        try {
            //实现拷贝处理
            //产生一个新的Student对象，并且拷贝原有对象的所有属性值
            student = (Student) super.clone();
            teacher = this.teacher.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }

    public Student cloneObject() throws Exception {
        //获取内存流
        ByteOutputStream bos = new ByteOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.getBytes());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (Student) ois.readObject();

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", teacher=" + teacher +
                '}';
    }
}
